/********************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2009.
-------------------------------------------------------------------------
File name:   AIDebugRenderer.cpp
$Id$
Description: Helper functions to draw some interesting debug shapes.

-------------------------------------------------------------------------
History:
- 2006-9-19   : Created (AIDebugDrawHelpers.cpp) by Mikko Mononen
- 2009-2-11   : class CAIVisualDebugRenderer by Evgeny Adamenkov

*********************************************************************/

#include "StdAfx.h"
#include "AIVisualDebugRenderer.h"

#include MATH_H
#include <IRenderer.h>
#include <Cry_Vector3.h>


CAIVisualDebugRenderer::CAIVisualDebugRenderer(IRenderer* pRenderer) :
	m_pRenderer(pRenderer),
	m_pRenderAuxGeom(pRenderer->GetIRenderAuxGeom())
{
}


//////////////////////////////////////////////////////////////////////////
//
//	IAIDebugRenderer interface methods

void CAIVisualDebugRenderer::DrawAABB(const AABB& aabb, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle)
{
	m_pRenderAuxGeom->DrawAABB(aabb, bSolid, color, bbDrawStyle);
}

void CAIVisualDebugRenderer::DrawAABB(const AABB& aabb, const Matrix34& matWorld, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle)
{
	m_pRenderAuxGeom->DrawAABB(aabb, matWorld, bSolid, color, bbDrawStyle);
}

//====================================================================
// DebugDrawArrow
//====================================================================
void CAIVisualDebugRenderer::DrawArrow(const Vec3& vPos, const Vec3& vLength, float fWidth, const ColorB& color)
{
	Vec3 points[7];
	Vec3 tris[5 * 3];

	float	len = vLength.GetLength();
	if (len < 0.0001f)
		return;

	float	headLen = fWidth * 2.0f;
	float	headWidth = fWidth * 2.0f;

	if (headLen > len * 0.8f)
	{
		headLen = len * 0.8f;
	}

	Vec3	vDir(vLength/len);
	Vec3	norm(vLength.y, -vLength.x, 0);
	norm.NormalizeSafe();

	Vec3	end(vPos + vLength);
	Vec3	start(vPos);

	unsigned int n = 0;
	points[n++] = end;
	points[n++] = end - vDir * headLen - norm * headWidth/2;
	points[n++] = end - vDir * headLen - norm * fWidth/2;
	points[n++] = end - vDir * headLen + norm * fWidth/2;
	points[n++] = end - vDir * headLen + norm * headWidth/2;
	points[n++] = start - norm * fWidth/2;
	points[n++] = start + norm * fWidth/2;

	n = 0;
	tris[n++] = points[0];
	tris[n++] = points[1];
	tris[n++] = points[2];

	tris[n++] = points[0];
	tris[n++] = points[2];
	tris[n++] = points[3];

	tris[n++] = points[0];
	tris[n++] = points[3];
	tris[n++] = points[4];

	tris[n++] = points[2];
	tris[n++] = points[5];
	tris[n++] = points[6];

	tris[n++] = points[2];
	tris[n++] = points[6];
	tris[n++] = points[3];

	DrawTriangles(tris, n, color);
}

//====================================================================
// DebugDrawCapsuleOutline
//====================================================================
void CAIVisualDebugRenderer::DrawCapsuleOutline(const Vec3& vPos0, const Vec3& vPos1, float fRadius, const ColorB& color)
{
	Vec3 points[20];
	Vec3 axisy = vPos1 - vPos0;
	axisy.Normalize();
	Vec3 axisx(axisy.y, -axisy.x, 0);
	axisx.Normalize();

	for (unsigned int i = 0; i < 10; i++)
	{
		float	a = ((float)i / 9.0f) * gf_PI;
		points[i] = vPos1 + axisx*cosf(a)*fRadius + axisy*sinf(a)*fRadius;
	}

	for (unsigned int i = 0; i < 10; i++)
	{
		float	a = gf_PI + ((float)i / 9.0f) * gf_PI;
		points[i+10] = vPos0 + axisx*cosf(a)*fRadius + axisy*sinf(a)*fRadius;
	}

	DrawPolyline(points, 20, true, color);
}

//====================================================================
// DebugDrawCircleOutline
//====================================================================
void CAIVisualDebugRenderer::DrawCircleOutline(const Vec3& vPos, float fRadius, const ColorB& color)
{
	Vec3 points[20];

	for (unsigned int i = 0; i < 20; i++)
	{
		float	a = ((float)i / 20.0f) * gf_PI2;
		points[i] = vPos + Vec3(cosf(a) * fRadius, sinf(a) * fRadius, 0);
	}
	DrawPolyline(points, 20, true, color);
}

//====================================================================
// DebugDrawCircles
//====================================================================
void CAIVisualDebugRenderer::DrawCircles(const Vec3& vPos,
																	 float fMinRadius, float fMaxRadius, int numRings,
																	 const ColorB& insideColor, const ColorB& outsideColor)
{
	static int numPts = 32;
	static std::vector<Vec3> unitCircle;
	static bool init = false;

	if (!init)
	{
		init = true;
		unitCircle.reserve(numPts);
		for (int i = 0 ; i < numPts ; ++i)
		{
			float angle = gf_PI2 * ((float) i) / numPts;
			unitCircle.push_back(Vec3(sinf(angle), cosf(angle), 0.0f));
		}
	}
	
	for (int iRing = 0 ; iRing < numRings ; ++iRing)
	{
		float ringFrac = 0.0f;
		if (numRings > 1)
		{
			ringFrac += ((float) iRing) / (numRings - 1);
		}

		float fRadius = (1.0f - ringFrac) * fMinRadius + ringFrac * fMaxRadius;
		ColorB col = (1.0f - ringFrac) * insideColor.toVec3() + ringFrac * outsideColor.toVec3();
		col.a = 255;

		Vec3 prevPt = vPos + fRadius * unitCircle[numPts - 1];
		prevPt.z = GetDebugDrawZ(prevPt, true);
		for (int i = 0 ; i < numPts ; ++i)
		{
			Vec3 pt = vPos + fRadius * unitCircle[i];
			pt.z = GetDebugDrawZ(pt, true) + 0.05f;

			DrawLine(prevPt, col, pt, col);
			prevPt = pt;
		}
	}
}

void CAIVisualDebugRenderer::DrawCone(const Vec3& vPos, const Vec3& vDir, float fRadius, float fHeight, const ColorB& color, bool bDrawShaded /* = true */)
{
	m_pRenderAuxGeom->DrawCone(vPos, vDir, fRadius, fHeight, color, bDrawShaded);
}

void CAIVisualDebugRenderer::DrawCylinder(const Vec3& vPos, const Vec3& vDir, float fRadius, float fHeight, const ColorB& color, bool bDrawShaded /* = true */)
{
	m_pRenderAuxGeom->DrawCylinder(vPos, vDir, fRadius, fHeight, color, bDrawShaded);
}

//====================================================================
// DebugDrawEllipseOutline
//====================================================================
void CAIVisualDebugRenderer::DrawEllipseOutline(const Vec3& vPos, float fRadiusX, float fRadiusY, float fOrientation, const ColorB& color)
{
	Vec3	points[20];

	float sin_o = sinf( fOrientation );
	float cos_o = cosf( fOrientation );
	for (unsigned int i = 0; i < 20; i++ )
	{
		float angle = ( (float) i / 20.0f ) * gf_PI2;
		float sin_a = sinf( angle );
		float cos_a = cosf( angle );
		float x = ( cos_o * cos_a * fRadiusX ) - ( sin_o * sin_a * fRadiusY );
		float y = ( cos_o * sin_a * fRadiusY ) + ( sin_o * cos_a * fRadiusX );
		points[i] = vPos + Vec3( x, y, 0.0f );
	}

	DrawPolyline( points, 20, true, color );
}

//====================================================================
// DebugDrawLabel
//====================================================================
void CAIVisualDebugRenderer::Draw2dLabel(int nCol, int nRow, const char* szText, const ColorB& color)
{
	float ColumnSize = 11;
	float RowSize = 11;
	float baseY = 10;
	float p_fColor[4] = {
		color.r / 255.0f,
		color.g / 255.0f,
		color.b / 255.0f,
		color.a / 255.0f
	};

	m_pRenderer->Draw2dLabel(ColumnSize*static_cast<float>(nCol), baseY+RowSize*static_cast<float>(nRow), 1.2f, p_fColor, false, "%s", szText);
}

void CAIVisualDebugRenderer::Draw2dLabel(float fX, float fY, float fFontSize, const ColorB& color, bool bCenter, const char* format, ...)
{
	va_list args;
	va_start(args, format);

	// Copy-pasted from IRenderer.h to use "va_list args"
	SDrawTextInfo ti;
	ti.xscale = ti.yscale = fFontSize;
	ti.flags = eDrawText_2D | eDrawText_800x600 | eDrawText_FixedSize | (bCenter ? eDrawText_Center : 0);
	ti.color[0] = color.r / 255.0f;
	ti.color[1] = color.g / 255.0f;
	ti.color[2] = color.b / 255.0f;
	ti.color[3] = color.a / 255.0f;
	m_pRenderer->DrawTextQueued(Vec3(fX, fY, 0.5f), ti, format, args);

	va_end(args);
}

void CAIVisualDebugRenderer::Draw3dLabel(const Vec3& vPos, float fFontSize, const char* format, ...)
{
	va_list args;
	va_start(args, format);

	// Copy-pasted from IRenderer.h to use "va_list args"
	SDrawTextInfo ti;
	ti.xscale = ti.yscale = fFontSize;
	ti.flags = eDrawText_FixedSize | eDrawText_800x600;
	m_pRenderer->DrawTextQueued(vPos, ti, format, args);

	va_end(args);
}

void CAIVisualDebugRenderer::Draw3dLabelEx(const Vec3& vPos, float fFontSize, const ColorB& color, bool bFixedSize, bool bCenter, const char* format, ...)
{
	va_list args;
	va_start(args, format);

	// Copy-pasted from IRenderer.h to use "va_list args"
	SDrawTextInfo ti;
	ti.xscale = ti.yscale = fFontSize;
	ti.flags = (bFixedSize ? eDrawText_FixedSize : 0) | (bCenter ? eDrawText_Center : 0) | eDrawText_800x600;
	ti.color[0] = color.r / 255.0f;
	ti.color[1] = color.g / 255.0f;
	ti.color[2] = color.b / 255.0f;
	ti.color[3] = color.a / 255.0f;
	m_pRenderer->DrawTextQueued(vPos, ti, format, args);

	va_end(args);
}

void CAIVisualDebugRenderer::Draw2dImage(float fX, float fY, float fWidth, float fHeight, int nTextureID, float fS0 /* = 0.f */, float fT0 /* = 0.f */, float fS1 /* = 1.f */, float fT1 /* = 1.f */, float fAngle /* = 0.f */, float fR /* = 1.f */, float fG /* = 1.f */, float fB /* = 1.f */, float fA /* = 1.f */, float fZ /* = 1.f */)
{
	m_pRenderer->Draw2dImage(fX, fY, fWidth, fHeight, nTextureID, fS0, fT0, fS1, fT1, fAngle, fR, fG, fB, fA, fZ);
}

void CAIVisualDebugRenderer::DrawLine(const Vec3& v0, const ColorB& colorV0, const Vec3& v1, const ColorB& colorV1, float fThickness /* = 1.f */)
{
	m_pRenderAuxGeom->DrawLine(v0, colorV0, v1, colorV1, fThickness);
}

void CAIVisualDebugRenderer::DrawOBB(const OBB& obb, const Vec3& vPos, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle)
{
	m_pRenderAuxGeom->DrawOBB(obb, vPos, bSolid, color, bbDrawStyle);
}

void CAIVisualDebugRenderer::DrawOBB(const OBB& obb, const Matrix34& matWorld, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle)
{
	m_pRenderAuxGeom->DrawOBB(obb, matWorld, bSolid, color, bbDrawStyle);
}

void CAIVisualDebugRenderer::DrawPolyline(const Vec3* va, uint32 nPoints, bool bClosed, const ColorB& color, float fThickness /* = 1.f */)
{
	m_pRenderAuxGeom->DrawPolyline(va, nPoints, bClosed, color, fThickness);
}

void CAIVisualDebugRenderer::DrawPolyline(const Vec3* va, uint32 nPoints, bool bClosed, const ColorB* colorArray, float fThickness /* = 1.f */)
{
	m_pRenderAuxGeom->DrawPolyline(va, nPoints, bClosed, colorArray, fThickness);
}

//====================================================================
// DebugDrawRangeArc
//====================================================================
void CAIVisualDebugRenderer::DrawRangeArc(const Vec3& vPos, const Vec3& vDir, float fAngle, float fRadius, float fWidth,
																		const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline)
{
	const unsigned npts = 12;

	Vec3	points[npts];
	Vec3	pointsOutline[npts];
	Vec3	tris[(npts - 1) * 2 * 3];

	Vec3	forw(vDir.x, vDir.y, 0.0f);
	forw.NormalizeSafe();
	Vec3	right(forw.y, -forw.x, 0);

	if (fWidth > fRadius)
	{
		fWidth = fRadius;
	}

	for (unsigned int i = 0; i < npts; i++)
	{
		float	a = ((float)i / (float)(npts - 1) - 0.5f) * fAngle;
		points[i] = forw * cosf(a) + right * sinf(a);
		pointsOutline[i] = vPos + points[i] * fRadius;
	}

	unsigned int n = 0;
	for (unsigned int i = 0; i < npts - 1; i++)
	{
		tris[n++] = vPos + points[i] * (fRadius - fWidth);
		tris[n++] = vPos + points[i+1] * fRadius;
		tris[n++] = vPos + points[i] * fRadius;

		tris[n++] = vPos + points[i] * (fRadius - fWidth);
		tris[n++] = vPos + points[i+1] * (fRadius - fWidth);
		tris[n++] = vPos + points[i+1] * fRadius;
	}

	DrawTriangles(tris, n, colorFill);
	if (bDrawOutline)
	{
		DrawPolyline(pointsOutline, npts, false, colorOutline);
		DrawLine(vPos + forw * (fRadius - fWidth/4), colorOutline, vPos + forw * (fRadius + fWidth/4), colorOutline);
	}
}

//====================================================================
// DebugDrawRange
//====================================================================
void CAIVisualDebugRenderer::DrawRangeBox(const Vec3& vPos, const Vec3& vDir, float fSizeX, float fSizeY, float fWidth,
																		const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline)
{
	float	minX = fSizeX - fWidth;
	float	maxX = fSizeX;
	float	minY = fSizeY - fWidth;
	float	maxY = fSizeY;

	if (maxX < 0.001f || maxY < 0.001f ||minX > maxX || minY > maxY)
		return;

	Vec3	points[8];
	Vec3	tris[8 * 3];
	Vec3	norm(vDir.y, -vDir.x, vDir.z);

	points[0] = vPos + norm * -minX + vDir * -minY;
	points[1] = vPos + norm *  minX + vDir * -minY;
	points[2] = vPos + norm *  minX + vDir *  minY;
	points[3] = vPos + norm * -minX + vDir *  minY;

	points[4] = vPos + norm * -maxX + vDir * -maxY;
	points[5] = vPos + norm *  maxX + vDir * -maxY;
	points[6] = vPos + norm *  maxX + vDir *  maxY;
	points[7] = vPos + norm * -maxX + vDir *  maxY;

	unsigned int n = 0;

	tris[n++] = points[0];
	tris[n++] = points[5];
	tris[n++] = points[1];
	tris[n++] = points[0];
	tris[n++] = points[4];
	tris[n++] = points[5];

	tris[n++] = points[1];
	tris[n++] = points[6];
	tris[n++] = points[2];
	tris[n++] = points[1];
	tris[n++] = points[5];
	tris[n++] = points[6];

	tris[n++] = points[2];
	tris[n++] = points[7];
	tris[n++] = points[3];
	tris[n++] = points[2];
	tris[n++] = points[6];
	tris[n++] = points[7];

	tris[n++] = points[3];
	tris[n++] = points[4];
	tris[n++] = points[0];
	tris[n++] = points[3];
	tris[n++] = points[7];
	tris[n++] = points[4];

	DrawTriangles(tris, 8 * 3, colorFill);
	if (bDrawOutline)
	{
		DrawPolyline(&points[4], 4, true, colorOutline);
	}
}

//====================================================================
// DebugDrawRangeCircle
//====================================================================
void CAIVisualDebugRenderer::DrawRangeCircle(const Vec3& vPos, float fRadius, float fWidth,
													const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline)
{
	const unsigned npts = 24;

	Vec3	points[npts];
	Vec3	pointsOutline[npts];
	Vec3	tris[npts * 2 * 3];

	if (fWidth > fRadius)
	{
		fWidth = fRadius;
	}

	for (unsigned int i = 0; i < npts; i++)
	{
		float	a = ((float)i / (float)npts) * gf_PI2;
		points[i] = Vec3(cosf(a), sinf(a), 0);
		pointsOutline[i] = vPos + points[i] * fRadius;
	}

	unsigned int n = 0;
	for (unsigned int i = 0; i < npts; i++)
	{
		tris[n++] = vPos + points[i] * (fRadius - fWidth);
		tris[n++] = vPos + points[i] * fRadius;
		tris[n++] = vPos + points[(i+1) % npts] * fRadius;

		tris[n++] = vPos + points[i] * (fRadius - fWidth);
		tris[n++] = vPos + points[(i+1) % npts] * fRadius;
		tris[n++] = vPos + points[(i+1) % npts] * (fRadius - fWidth);
	}

	DrawTriangles(tris, npts * 2 * 3, colorFill);
	if (bDrawOutline)
	{
		DrawPolyline(pointsOutline, npts, true, colorOutline);
	}
}

//====================================================================
// DebugDrawRangePolygon
//====================================================================
void CAIVisualDebugRenderer::DrawRangePolygon(const Vec3* va, uint32 nPoints, float fWidth,
													 const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline)
{
	static std::vector<Vec3>		verts;
	static std::vector<uint16>	tris;
	static std::vector<Vec3>		outline;

	if (nPoints < 3)
		return;

	Vec3	prevDir(va[0] - va[nPoints - 1]);
	prevDir.NormalizeSafe();
	Vec3	prevNorm(-prevDir.y, prevDir.x, 0.0f);
	prevNorm.NormalizeSafe();
	Vec3	prevPos(va[nPoints - 1]);

	verts.clear();
	outline.clear();

	const Vec3* li, * linext;
	const Vec3* liend = va + nPoints;
	for (li = va; li != liend ; ++li)
	{
		linext = li;
		++linext;
		if (linext == liend)
		{
			linext = va;
		}

		const Vec3&	curPos(*li);
		const Vec3&	nextPos(*linext);
		Vec3	vDir(nextPos - curPos);
		Vec3	norm(-vDir.y, vDir.x, 0.0f);
		norm.NormalizeSafe();

		Vec3	mid((prevNorm + norm) * 0.5f);
		float	dmr2 = sqr(mid.x) + sqr(mid.y);
		if (dmr2 > 0.00001f)
		{
			mid *= 1.0f / dmr2;
		}

		float	cross = prevDir.x * vDir.y - vDir.x * prevDir.y;

		outline.push_back(curPos);

		if (cross < 0.0f)
		{
			if (dmr2 * sqr(2.5f) < 1.0f)
			{
				// bevel
				verts.push_back(curPos);
				verts.push_back(curPos + prevNorm * fWidth);
				verts.push_back(curPos);
				verts.push_back(curPos + norm * fWidth);
			}
			else
			{
				verts.push_back(curPos);
				verts.push_back(curPos + mid * fWidth);
			}
		}
		else
		{
			verts.push_back(curPos);
			verts.push_back(curPos + mid * fWidth);
		}

		prevDir = vDir;
		prevNorm = norm;
		prevPos = curPos;
	}

	tris.clear();
	size_t	n = verts.size()/2;
	for (size_t i = 0; i < n; ++i)
	{
		size_t	j = (i + 1) % n;
		tris.push_back(i*2);
		tris.push_back(j*2);
		tris.push_back(j*2+1);

		tris.push_back(i*2);
		tris.push_back(j*2+1);
		tris.push_back(i*2+1);
	}

	m_pRenderAuxGeom->DrawTriangles(&verts[0], verts.size(), &tris[0], tris.size(), colorFill);
	if (bDrawOutline)
	{
		DrawPolyline(&outline[0], outline.size(), true, colorOutline);
	}
}


void CAIVisualDebugRenderer::DrawSphere(const Vec3& vPos, float fRadius, const ColorB& color, bool bDrawShaded /* = true */)
{
	m_pRenderAuxGeom->DrawSphere(vPos, fRadius, color, bDrawShaded);
}

void CAIVisualDebugRenderer::DrawTriangle(const Vec3& v0, const ColorB& colorV0, const Vec3& v1, const ColorB& colorV1, const Vec3& v2, const ColorB& colorV2)
{
	m_pRenderAuxGeom->DrawTriangle(v0, colorV0, v1, colorV1, v2, colorV2);
}

void CAIVisualDebugRenderer::DrawTriangles(const Vec3* va, uint32 numPoints, const ColorB& color)
{
	m_pRenderAuxGeom->DrawTriangles(va, numPoints, color);
}

//====================================================================
// DebugDrawWireFOVCone
//====================================================================
void CAIVisualDebugRenderer::DrawWireFOVCone(const Vec3& vPos, const Vec3& vDir, float fRadius, float fFOV, const ColorB& color)
{
	const unsigned int npts = 32;
	const unsigned int npts2 = 16;
	Vec3	points[npts];
	Vec3	pointsx[npts2];
	Vec3	pointsy[npts2];

	Matrix33	base;
	base.SetRotationVDir(vDir);

	float coneRadius = sinf(fFOV) * fRadius;
	float coneHeight = cosf(fFOV) * fRadius;

	for (unsigned int i = 0; i < npts; i++)
	{
		float	a = ((float)i / (float)npts) * gf_PI2;
		float rx = cosf(a) * coneRadius;
		float ry = sinf(a) * coneRadius;
		points[i] = vPos + base.TransformVector(Vec3(rx, coneHeight, ry));
	}

	for (unsigned int i = 0; i < npts2; i++)
	{
		float	a = -fFOV + ((float)i / (float)(npts2-1)) * (fFOV*2);
		float rx = sinf(a) * fRadius;
		float ry = cosf(a) * fRadius;
		pointsx[i] = vPos + base.TransformVector(Vec3(rx, ry, 0));
		pointsy[i] = vPos + base.TransformVector(Vec3(0, ry, rx));
	}

	DrawPolyline(points, npts, true, color);
	DrawPolyline(pointsx, npts2, false, color);
	DrawPolyline(pointsy, npts2, false, color);

	DrawLine(points[0], color, vPos, color);
	DrawLine(points[npts/4], color, vPos, color);
	DrawLine(points[npts/2], color, vPos, color);
	DrawLine(points[npts/2+npts/4], color, vPos, color);
}

//====================================================================
// DebugDrawWireSphere
//====================================================================
void CAIVisualDebugRenderer::DrawWireSphere(const Vec3& vPos, float fRadius, const ColorB& color)
{
	const unsigned int npts = 32;
	Vec3	xpoints[npts];
	Vec3	ypoints[npts];
	Vec3	zpoints[npts];

	for (unsigned int i = 0; i < npts; i++)
	{
		float	a = ((float)i / (float)npts) * gf_PI2;
		float rx = cosf(a) * fRadius;
		float ry = sinf(a) * fRadius;
		xpoints[i] = vPos + Vec3(rx, ry, 0);
		ypoints[i] = vPos + Vec3(0, rx, ry);
		zpoints[i] = vPos + Vec3(ry, 0, rx);
	}

	DrawPolyline(xpoints, npts, true, color);
	DrawPolyline(ypoints, npts, true, color);
	DrawPolyline(zpoints, npts, true, color);
}

ITexture* CAIVisualDebugRenderer::LoadTexture(const char* szNameOfTexture, uint32 nFlags)
{
	return m_pRenderer->EF_LoadTexture(szNameOfTexture, nFlags);
}

bool CAIVisualDebugRenderer::ProjectToScreen(float fInX, float fInY, float fInZ, float* pfOutX, float* pfOutY, float* pfOutZ)
{
	return m_pRenderer->ProjectToScreen(fInX, fInY, fInZ, pfOutX, pfOutY, pfOutZ);
}

void CAIVisualDebugRenderer::TextToScreen(float fX, float fY, const char* format, ...)
{
	// Copy-pasted from Renderer.cpp
	char buffer[512];
	va_list args;
	va_start(args, format);
	if (vsnprintf(buffer, sizeof buffer, format, args) == -1)
	{
		buffer[sizeof buffer - 1] = 0;
	}
	va_end(args);

	m_pRenderer->TextToScreen(fX, fY, "%s", buffer);
}

void CAIVisualDebugRenderer::TextToScreenColor(int nX, int nY, float fRed, float fGreen, float fBlue, float fAlpha, const char* format, ...)
{
	// Copy-pasted from Renderer.cpp
	char buffer[512];
	va_list args;
	va_start(args, format);
	if (vsnprintf(buffer, sizeof buffer, format, args) == -1)
	{
		buffer[sizeof buffer - 1] = 0;
	}
	va_end(args);
	
	m_pRenderer->TextToScreenColor(nX, nY, fRed, fGreen, fBlue, fAlpha, buffer);
}

void CAIVisualDebugRenderer::SetMaterialColor(float fRed, float fGreen, float fBlue, float fAlpha)
{
	m_pRenderer->SetMaterialColor(fRed, fGreen, fBlue, fAlpha);
}

void CAIVisualDebugRenderer::SetFlags(SAuxGeomRenderFlags flags)
{
	CAIAbstractDebugRenderer::SetFlags(flags);	// Update the flag stack
	m_pRenderer->GetIRenderAuxGeom()->SetRenderFlags(flags);
}

int CAIVisualDebugRenderer::GetFrameID()
{
	return m_pRenderer->GetFrameID();
}

void CAIVisualDebugRenderer::DebugDraw()
{
	CDrawCommandsInputStream& s = m_inputStream;	// to save me some typing

	s.Lock();

	if (s.Empty())
	{
		s.Unlock();
		return;
	}

	static ICVar* cvar_ai_NetworkDebug = gEnv->pConsole->GetCVar("ai_NetworkDebug");
	if (cvar_ai_NetworkDebug->GetIVal() == 2)
	{
		TextToScreen(10, 10, "AI Network Debug: Delivery: %u, bytes received: %u.", (unsigned int)s.m_deliveryID, (unsigned int)s.Size());
		if (size_t deliveryRate = s.m_deliveryRate)
		{
			TextToScreen(10, 12, "AI Network Debug: Current transmission speed: %d bytes per second.", (int)deliveryRate);
		}
		else
		{
			TextToScreen(10, 12, "AI Network Debug: Current transmission speed: unknown.");
		}
	}

	s.Rewind();
	
	while (s.HasMoreCommands())
	{
		EDrawCommandType type = s;
		
		switch (type)
		{
			case eDrawAABB:
			{
				const AABB& aabb = s; bool bSolid = s; const ColorB& color = s; EBoundingBoxDrawStyle bbDrawStyle = s;
				DrawAABB(aabb, bSolid, color, bbDrawStyle);
				continue;
			}
			case eDrawAABB_matrix:
			{
				const AABB& aabb = s; const Matrix34& matWorld = s; bool bSolid = s; const ColorB& color = s; EBoundingBoxDrawStyle bbDrawStyle = s;
				DrawAABB(aabb, matWorld, bSolid, color, bbDrawStyle);
				continue;
			}
			case eDrawArrow:
			{
				const Vec3& vPos = s; const Vec3& vLength = s; float fWidth = s; const ColorB& color = s;
				DrawArrow(vPos, vLength, fWidth, color);
				continue;
			}
			case eDrawCapsuleOutline:
			{
				const Vec3& vPos0 = s; const Vec3& vPos1 = s; float fRadius = s; const ColorB& color = s;
				DrawCapsuleOutline(vPos0, vPos1, fRadius, color);
				continue;
			}
			case eDrawCircleOutline:
			{
				const Vec3& vPos = s; float fRadius = s; const ColorB& color = s;
				DrawCircleOutline(vPos, fRadius, color);
				continue;
			}
			case eDrawCircles:
			{
				const Vec3& vPos = s; float fMinRadius = s; float fMaxRadius = s; int numRings = s; const Vec3& vInsideColor = s; const Vec3& vOutsideColor = s;
				DrawCircles(vPos, fMinRadius, fMaxRadius, numRings, vInsideColor, vOutsideColor);
				continue;
			}
			case eDrawCone:
			{
				const Vec3& vPos = s; const Vec3& vDir = s; float fRadius = s; float fHeight = s; const ColorB& color = s; bool bDrawShaded = s;
				DrawCone(vPos, vDir, fRadius, fHeight, color, bDrawShaded);
				continue;
			}
			case eDrawCylinder:
			{
				const Vec3& vPos = s; const Vec3& vDir = s; float fRadius = s; float fHeight = s; const ColorB& color = s; bool bDrawShaded = s;
				DrawCylinder(vPos, vDir, fRadius, fHeight, color, bDrawShaded);
				continue;
			}
			case eDrawEllipseOutline:
			{
				const Vec3& vPos = s; float fRadiusX = s; float fRadiusY = s; float fOrientation = s; const ColorB& color = s;
				DrawEllipseOutline(vPos, fRadiusX, fRadiusY, fOrientation, color);
				continue;
			}
			case eDraw2dLabelSimple:
			{
				int nCol = s; int nRow = s; const char* szText = s; const ColorB& color = s;
				Draw2dLabel(nCol, nRow, szText, color);
				continue;
			}
			case eDraw2dLabel:
			{
				float fX = s; float fY = s; float fFontSize = s; const ColorB& color = s; bool bCenter = s; const char* text = s;
				if ((0.f <= m_vProjectedPoint.z) && (m_vProjectedPoint.z <= 1.f))
				{
					Draw2dLabel(
						fX - m_vPointToProject.x + m_vProjectedPoint.x,
						fY - m_vPointToProject.y + m_vProjectedPoint.y,
						fFontSize, color, bCenter, text
					);
				}
				continue;
			}
			case eDraw3dLabel:
			{
				const Vec3& vPos = s; float fFontSize = s; const char* text = s;
				Draw3dLabel(vPos, fFontSize, text);
				continue;
			}
			case eDraw3dLabelEx:
			{
				const Vec3& vPos = s; float fFontSize = s; const ColorB& color = s; bool bFixedSize = s; bool bCenter = s; const char* text = s;
				Draw3dLabelEx(vPos, fFontSize, color, bFixedSize, bCenter, text);
				continue;
			}
			case eDraw2dImage:
			{
				float fX = s; float fY = s; float fWidth = s; float fHeight = s; int nTextureID = s; float fS0 = s; float fT0 = s; float fS1 = s; float fT1 = s; float fAngle = s; float fR = s; float fG = s; float fB = s; float fA = s; float fZ = s;
				if (int nActualTextureID = m_mapActualTextureID[nTextureID])
				{
					Draw2dImage(fX, fY, fWidth, fHeight, nActualTextureID, fS0, fT0, fS1, fT1, fAngle, fR, fG, fB, fA, fZ);
				}
				continue;
			}
			case eDrawLine:
			{
				const Vec3& v0 = s; const ColorB& colorV0 = s; const Vec3& v1 = s; const ColorB& colorV1 = s; float fThickness = s;
				DrawLine(v0, colorV0, v1, colorV1, fThickness);
				continue;
			}
			case eDrawOBB_matrix:
			{
				const OBB& obb = s; const Matrix34& matWorld = s; bool bSolid = s; const ColorB& color = s; EBoundingBoxDrawStyle bbDrawStyle = s;
				DrawOBB(obb, matWorld, bSolid, color, bbDrawStyle);
				continue;
			}
			case eDrawOBB_vector:
			{
				const OBB& obb = s; const Vec3& vPos = s; bool bSolid = s; const ColorB& color = s; EBoundingBoxDrawStyle bbDrawStyle = s;
				DrawOBB(obb, vPos, bSolid, color, bbDrawStyle);
				continue;
			}
			case eDrawPolyline_single_color:
			{
				// nPoints should come before va
				uint32 nPoints = s; const Vec3* va = s.Read<Vec3>(nPoints); bool bClosed = s; const ColorB& color = s; float fThickness = s;
				DrawPolyline(va, nPoints, bClosed, color, fThickness);
				continue;
			}
			case eDrawPolyline_multiple_colors:
			{
				// nPoints should come before va
				uint32 nPoints = s; const Vec3* va = s.Read<Vec3>(nPoints); bool bClosed = s; const ColorB* colorArray = s.Read<ColorB>(nPoints); float fThickness = s;
				DrawPolyline(va, nPoints, bClosed, colorArray, fThickness);
				continue;
			}
			case eDrawRangeArc:
			{
				const Vec3& vPos = s; const Vec3& vDir = s; float fAngle = s; float fRadius = s; float fWidth = s; const ColorB& colorFill = s; const ColorB& colorOutline = s; bool bDrawOutline = s;
				DrawRangeArc(vPos, vDir, fAngle, fRadius, fWidth, colorFill, colorOutline, bDrawOutline);
				continue;
			}
			case eDrawRangeBox:
			{
				const Vec3& vPos = s; const Vec3& vDir = s; float fSizeX = s; float fSizeY = s; float fWidth = s; const ColorB& colorFill = s; const ColorB& colorOutline = s; bool bDrawOutline = s;
				DrawRangeBox(vPos, vDir, fSizeX, fSizeY, fWidth, colorFill, colorOutline, bDrawOutline);
				continue;
			}
			case eDrawRangeCircle:
			{
				const Vec3& vPos = s; float fRadius = s; float fWidth = s; const ColorB& colorFill = s; const ColorB& colorOutline = s; bool bDrawOutline = s;
				DrawRangeCircle(vPos, fRadius, fWidth, colorFill, colorOutline, bDrawOutline);
				continue;
			}
			case eDrawRangePolygon:
			{
				// nPoints should come before va
				uint32 nPoints = s; const Vec3* va = s.Read<Vec3>(nPoints); float fWidth = s; const ColorB& colorFill = s; const ColorB& colorOutline = s; bool bDrawOutline = s;
				DrawRangePolygon(va, nPoints, fWidth, colorFill, colorOutline, bDrawOutline);
				continue;
			}
			case eDrawSphere:
			{
				const Vec3& vPos = s; float fRadius = s; const ColorB& color = s; bool bDrawShaded = s;
				DrawSphere(vPos, fRadius, color, bDrawShaded);
				continue;
			}
			case eDrawTriangle:
			{
				const Vec3& v0 = s; const ColorB& colorV0 = s; const Vec3& v1 = s; const ColorB& colorV1 = s; const Vec3& v2 = s; const ColorB& colorV2 = s;
				DrawTriangle(v0, colorV0, v1, colorV1, v2, colorV2);
				continue;
			}
			case eDrawTriangles:
			{
				// numPoints should come before va
				uint32 numPoints = s; const Vec3* va = s.Read<Vec3>(numPoints); const ColorB& color = s;
				DrawTriangles(va, numPoints, color);
				continue;
			}
			case eDrawWireFOVCone:
			{
				const Vec3& vPos = s; const Vec3& vDir = s; float fRadius = s; float fFOV = s; const ColorB& color = s;
				DrawWireFOVCone(vPos, vDir, fRadius, fFOV, color);
				continue;
			}
			case eDrawWireSphere:
			{
				const Vec3& vPos = s; float fRadius = s; const ColorB& color = s;
				DrawWireSphere(vPos, fRadius, color);
				continue;
			}
			case eLoadTexture:
			{
				const char* szNameOfTexture = s; uint32 nFlags = s; int nTextureID = s;
				m_mapActualTextureID[nTextureID] = LoadTexture(szNameOfTexture, nFlags)->GetTextureID();
				continue;
			}
			case eSetFlags:
			{
				uint32 uiFlags = s;
				SetFlags(uiFlags);
				continue;
			}
			case eSetMaterialColor:
			{
				float fRed = s; float fGreen = s; float fBlue = s; float fAlpha = s;
				SetMaterialColor(fRed, fGreen, fBlue, fAlpha);
				continue;
			}
			case eTextToScreen:
			{
				float fX = s; float fY = s; const char* text = s;
				TextToScreen(fX, fY, text);
				continue;
			}
			case eTextToScreenColor:
			{
				int nX = s; int nY = s; float fRed = s; float fGreen = s; float fBlue = s; float fAlpha = s; const char* text = s;
				TextToScreenColor(nX, nY, fRed, fGreen, fBlue, fAlpha, text);
				continue;
			}
			case eProjectToScreen:
			{
				m_vPointToProject.x = s;
				m_vPointToProject.y = s;
				m_vPointToProject.z = s;
				
				ProjectToScreen(
					m_vPointToProject.x,
					m_vPointToProject.y,
					m_vPointToProject.z,
					
					&m_vProjectedPoint.x,
					&m_vProjectedPoint.y,
					&m_vProjectedPoint.z
				);

				m_vProjectedPoint.x *= .01f * GetWidth();
				m_vProjectedPoint.y *= .01f * GetHeight();

				continue;
			}
		}
		
		assert(!!!"Unrecognized Draw Command!");
		break;
	}

	s.SetNeedsEndianSwap(false);

	s.Unlock();
}



//////////////////////////////////////////////////////////////////////////
//
//	Network AI Debug Draw receiver

void CAIVisualDebugRenderer::ConnectReceiver(INotificationNetwork* pNotificationNetwork)
{
	assert(pNotificationNetwork);
	pNotificationNetwork->ListenerBind(CAIAbstractDebugRenderer::szNetworkChannelName, &m_inputStream);
}

void CAIVisualDebugRenderer::DisconnectReceiver(INotificationNetwork* pNotificationNetwork)
{
	assert(pNotificationNetwork);
	pNotificationNetwork->ListenerRemove(&m_inputStream);

	m_inputStream.Clear();
	m_mapActualTextureID.clear();
}
